package com.xx.utils.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author fmy
 * @date 2022/12/1 18:58
 * @desc RedisServiceUtil
 */
@Component
public class RedisUtil {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    public Map<String, Long> countHashKeys(List<String> hashKeys) {
        HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();
        Map<String, Long> counts = new HashMap<>();
        for (String hashKey : hashKeys) {
            Long count = hashOps.size(hashKey);
            counts.put(hashKey, count);
        }
        return counts;
    }

    public  Set<String> keys(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        return keys;
    }
    /**
     * 写入缓存
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 写入缓存设置时效时间
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 设置指定键的过期时间
     * @param key 键
     * @param expireTime 过期时间
     * @param timeUnit 时间单位
     * @return true：成功
     */
    public boolean setExpire(final String key, Long expireTime, TimeUnit timeUnit) {
        return redisTemplate.expire(key, expireTime, timeUnit);
    }

    /**
     * 批量删除对应的value
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 删除对应的value
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }
    /**
     * 判断缓存中是否有对应的value
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 读取缓存
     * @param key
     * @return
     */
    public <T> T get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        if (null == result) {
            return null;
        }
        return (T)result;
    }
    /**
     * 哈希 添加
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key,hashKey,value);
    }
    public void hmset(String key, Map<String, String> value) {
        redisTemplate.opsForHash().putAll(key, value);
    }

    public Map<String, String> hgetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 以map集合的形式添加键值对
     * @param key 键
     * @param map Map
     */
    public void hPutAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 哈希获取数据
     * @param key
     * @param hashKey
     * @return
     */
/*    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key,hashKey);
    }*/
    public <T> T hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return (T) hash.get(key, hashKey);
    }
    /**
     * 根据键获取到redis中存储的map
     * @param key 键
     * @return 键对应的map
     */
    public Map<String, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 根据键获取到redis中存储的map
     * 这个更加的严谨,对list每个元素进行强转
     */
    public <T> Map<String, T> hGetAll(String k, Class<T> tClass) {
        Map<String, Object> entries = redisTemplate.opsForHash().entries(k);
        Map<String, T> map = new HashMap<>();
        for (Map.Entry<String, Object> entry : entries.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof List) {
                List<?> list = (List<?>) value;
                // 判断集合中的元素是否为 tClass 类型
                if (!list.isEmpty() && tClass.isInstance(list)) {
                    map.put(key, (T) value);
                }
            } else if (tClass.isInstance(value)) {
                map.put(key, tClass.cast(value));
            }
        }
        return map;
    }
    /**
     * 自增/自减key对应map中的指定字段的long型数值
     * @param key 键
     * @param field key对应map中存储的字段
     * @param increment 正数-自增；负数-自减
     * @return 自增/自减后的数值
     */
    public Long hashIncrBy(String key, Object field, long increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 自增/自减key对应map中的指定字段的double型数值
     * @param key 键
     * @param field key对应map中存储的字段
     * @param delta 正数-自增；负数-自减
     * @return 自增/自减后的数值
     */
    public Double hIncrByDouble(String key, Object field, double delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 查询redis中指定字段是否存在
     * @param key 键
     * @param field key对应map中存储的字段
     * @return true:存在
     */
    public boolean hashExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 列表添加
     * @param k
     * @param v
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k,v);
    }

    /**
     * 列表获取
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k,l,l1);
    }

    /**
     * 集合添加
     * @param key
     * @param value
     */
    public void add(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key,value);
    }

    /**
     * 集合获取
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key,value,scoure);
    }
    public void zAddStr2(String key, String value, double scoure) {
        ZSetOperations<String, String> zset = stringRedisTemplate.opsForZSet();
        zset.add(key, value, scoure);
    }
    /**
     * 有序集合添加
     * @param key
     * @param value
     * @param scoure
     */
    public void zAddStr(String key, String value, double scoure) {
        ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
        zset.add(key,value,scoure);
    }

    /**
     * 统计(key对应的)zset中item的个数
     *
     * @param key 定位zset的key
     * @return zset中item的个数
     * @date 2023/4/23 12:20:43
     */
    public long zZCard(String key) {
        Long size = redisTemplate.opsForZSet().zCard(key);
        if (size == null) {
          return 0L;
        }
        return size;
    }
    /**
     * 有序集合获取
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }
    /**
     * 有序集合的范围删除
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Long removeRangeByScore(String key, double scoure, double scoure1) {
       return redisTemplate.opsForZSet().removeRangeByScore(key, scoure, scoure1);
    }


    /**
     * redis原子型自增
     * */
    public Long incr(String key){
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.getAndIncrement();
        return increment;
    }
    /**
     * Hash 递增，如果不存在则创建一个，并把新增的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   递增大小 > 0
     * @return
     */
    public long hIncr(String key, String item, long by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }
    /**
     * Hash 递减
     *
     * @param key  键
     * @param item 项
     * @param by   递减大小
     * @return
     */
    public long hDecr(String key, String item, long by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    /**
     * HyperLogLog计数
     * @param key
     * @param value
     */
    public Long addHyperLog(String key, Object... value) {
        return redisTemplate.opsForHyperLogLog().add(key,value);
    }

    /**
     * HyperLogLog获取总数
     * @param key
     */
    public Long count(String key) {
        return redisTemplate.opsForHyperLogLog().size(key);
    }

    /**
     * HyperLogLog获取总数
     * @param key
     */
    public Long count(String... key) {
        return redisTemplate.opsForHyperLogLog().size(key);
    }

    /**
     * 获取 key 对应的 map
     *
     * @param key 键（no null）
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmGet(String key) {
        return redisTemplate.opsForHash().entries(key);
    }


    /**
     * 删除缓存
     *
     * @param key 键（一个或者多个）
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                // 传入一个 Collection<String> 集合
                List keys = CollectionUtils.arrayToList(key);
                redisTemplate.delete(keys);
            }
        }
    }

    /**
     * lua 根据scope范围查询,每4个取1
     */
    public  List<Object> rangeByScore1_8(String key, double scoure, double scoure1) {
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>( "local min = tonumber(ARGV[1]);local max = tonumber(ARGV[2]);local values = redis.call('ZRANGEBYSCORE',KEYS[1], min, max, 'WITHSCORES');local result = {};for i = 1, #values, 8 do  table.insert(result, values[i]) end;return result" , List.class);
        // 执行Lua脚本
        return stringRedisTemplate.execute(redisScript, Collections.singletonList(key), scoure + "", scoure1 + "");
    }

    public List<Object> rangeByScoreN(String key, double score, double score1, int interval) {
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(
                "local min = tonumber(ARGV[1]);local max = tonumber(ARGV[2]);local interval = tonumber(ARGV[3]);local values = redis.call('ZRANGEBYSCORE', KEYS[1], min, max, 'WITHSCORES');local result = {};for i = 1, #values, interval do  table.insert(result, values[i]) end;return result",
                List.class
        );
        return stringRedisTemplate.execute(redisScript, Collections.singletonList(key), String.valueOf(score), String.valueOf(score1), String.valueOf(interval));
    }

    /**
     * lua 根据scope范围查询,每4个取1
     */
    public  List<String> rangeByScoreList1_4(String key, double scoure, double scoure1) {
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>( "local min = tonumber(ARGV[1]);local max = tonumber(ARGV[2]);local values = redis.call('ZRANGEBYSCORE',KEYS[1], min, max, 'WITHSCORES');local result = {};for i = 1, #values, 4 do  table.insert(result, values[i]) end;return result" , List.class);
        // 执行Lua脚本
        return stringRedisTemplate.execute(redisScript, Collections.singletonList(key), scoure + "", scoure1 + "");
    }
}
